﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using wrsm.common;
using wrsm.common.entity;
using wrsm.client.exceptions;
using wrsm.battlenet;
using wrsm.battlenet.entity;
using wrsm.battlenet.exceptions;

namespace wrsm.client {

    /// <summary>
    /// Monitors changes in realm status
    /// </summary>
    internal class RealmStatusMonitor {

        #region [Event Definitions]

        /// <summary>
        /// Event indicating that the status of a realm changed
        /// </summary>
        public event RealmStatusHandler RealmStatusChanged;

        /// <summary>
        /// Event indicating that the monitor has requested a realm status update
        /// </summary>
        public event GeneralHandler RealmStatusPullRequested;

        /// <summary>
        /// Event indicating that the monitor has compelted it's request to update status
        /// </summary>
        public event GeneralHandler RealmStatusPullCompleted;

        /// <summary>
        /// Event indicating that something bad has happened when requesting, and that it did not complete normally
        /// </summary>
        public event GeneralHandler RealmStatusPullFaulted;

        #endregion

        #region [Local Attributes]

        /// <summary>
        /// used to monitor realm status
        /// </summary>
        private RealmStatusGrabber _realmStatusGrabber;

        /// <summary>
        /// Collection of current realm status
        /// </summary>
        private Dictionary<string, RealmStatus> _currentStatus;

        /// <summary>
        /// Used to save and restore the most recent known status for realms
        /// </summary>
        private IEntityTransformer _realmStatusEntityTransformer;

        /// <summary>
        /// Used to store changes to realm status, for possible future historical data processing/charting/whatever
        /// </summary>
        private IRealmStatusHistoryTransformer _realmStatusHistoryTransformer;

        /// <summary>
        /// A bit indicating that we are restoring data, and do not need to save historical changes or anything
        /// </summary>
        private bool _restoreMode = false;

        #endregion

        #region [Public Properties]

        /// <summary>
        /// Determnies if Monitor is currently restoring data
        /// </summary>
        public bool RestoreMode {
            get {
                return _restoreMode;
            }
        }

        #endregion

        #region [Synchronization Locks]

        /// <summary>
        /// Used to lock thread access to Realm Status Update
        /// </summary>
        private object _statusUpdateLock = new object();

        #endregion

        #region [Construction]

        /// <summary>
        /// Initialize RelamStatusMonitor
        /// </summary>
        public RealmStatusMonitor() {
            _realmStatusGrabber = new RealmStatusGrabber();
            _currentStatus = new Dictionary<string, RealmStatus>();

            var fileTransformer = new RealmStatusFileTransformer();
            _realmStatusEntityTransformer = (IEntityTransformer)fileTransformer;
            _realmStatusHistoryTransformer = (IRealmStatusHistoryTransformer)fileTransformer; 
            WireupEvents();
        }

        #endregion

        #region [Event Implementation]

        /// <summary>
        /// Wire up all events to handlers
        /// </summary>
        private void WireupEvents() {
            RealmStatusChanged += new RealmStatusHandler(RealmStatusChangedHandler);
            RealmStatusPullRequested += new GeneralHandler(RealmStatusPullRequestedHandler);
            RealmStatusPullCompleted += new GeneralHandler(RealmStatusPullCompletedHandler);
            RealmStatusPullFaulted += new GeneralHandler(RealmStatusPullFaultedHandler);
        }

        /// <summary>
        /// Fires when a realm status has changed
        /// </summary>
        /// <param name="status">the new status for a realm</param>
        private void RealmStatusChangedHandler(RealmStatus status) {
            //I dont want to save any history, if we're in restore more
            if (_restoreMode) return;
            //Save this realm status Change
            _realmStatusHistoryTransformer.TransformRealmHistory(status);
        }

        /// <summary>
        /// Fires when realm status update has been requested
        /// </summary>
        private void RealmStatusPullRequestedHandler() {
        }

        /// <summary>
        /// Fires when realm status update request has completed
        /// </summary>
        private void RealmStatusPullCompletedHandler() {
            //Save all realm status's as current
            _realmStatusEntityTransformer.TransformOut(_currentStatus.Values);
        }

        /// <summary>
        /// Fires when realm statys update request has faulted in some way
        /// </summary>
        private void RealmStatusPullFaultedHandler() {
        }

        #endregion

        #region [Public Methods]

        /// <summary>
        /// Return all realm status objects
        /// </summary>
        /// <returns>a list of all realm status objects currently monitored</returns>
        public IEnumerable<RealmStatus> GetAllRealmStatus() {
            return _currentStatus.Values;
        }

        /// <summary>
        /// Requests realm status update from battle.net
        /// </summary>
        public void UpdateRealmStatus() {

            lock (_statusUpdateLock) {
                try {
                    //Notify that realm status request has been requested
                    RealmStatusPullRequested();
                    var statusResponse = _realmStatusGrabber.GetStatus();
                    foreach (var statusUpdate in statusResponse) {
                        AddRealmStatus(statusUpdate);
                    }
                    //Notify that realm status request has completed
                    RealmStatusPullCompleted();
                } catch (BattleNetApplicationException caught) {
                    //Notify that realm status request has faulted, and did not complete normally
                    RealmStatusPullFaulted();
                    throw new WrsmClientMonitoringException("Unable to get an update from battle.net", caught);
                }
            }
        }

        /// <summary>
        /// Attempt to restore saved realm data,
        /// This is typically desirable when the application loads.
        /// 
        /// Not only does it give you an immediate "last known" status, it also helps
        /// prevent needless historical stamping on the first pull. (as if that matters)
        /// </summary>
        public void RestoreSavedRealmData() {
            //Attempt to restore entities
            try {
                //Prevent saving history while loading
                _restoreMode = true;
                //Grab all realm status from saved file.
                IEnumerable<Entity> restoredData = _realmStatusEntityTransformer.TransformIn();
                RealmStatus status = null;
                //Attempt to add discovered realm statu's to the monitor list.
                foreach (var entity in restoredData) {
                    status = entity as RealmStatus;
                    AddRealmStatus(status);
                }
            } finally {
                //turn off restore more
                _restoreMode = false;
            }
        }
        

        #endregion

        #region [The Guts]

        /// <summary>
        /// Add/Update realm status to the monitor
        /// </summary>
        /// <param name="status">Realm status to monitor</param>
        private void AddRealmStatus(RealmStatus status) {
            if (_currentStatus.ContainsKey(status.Name)) {
                int compare = _currentStatus[status.Name].CompareTo(status);
                //Test to see if there has been a change in the status.
                if (compare != 0) {
                    _currentStatus[status.Name] = status;
                    RealmStatusChanged(status);
                }
            } else {
                _currentStatus[status.Name] = status;
                RealmStatusChanged(status);
            }
        }

        #endregion

    }
}
